const utils = require('./lib/utils')
const axios = require('axios');

// 定义策略对象，每个策略包含生成报告数据和处理推送结果的方法
const pushStrategies = {
    cainiao: {
        generateData: (report, c) => {
            const body = {
                supplier: report.cus_id,
                method: 'MD5',
                timestamp: new Date().getTime(),
                requestId: report.ref,
                phoneNumber: report.mobile,
                sendTime: report.send_date,
                reportTime: report.report_date,
                success: report.status === 'DELIVRD',
                senderId: report.cus_id,
                errorCode: report.status,
                errorMsg: report.status,
                smsSize: report.pieces,
                bizId: report.ticket,
                extend: report.ext
            };
            body.signature = utils.getSignature(body, c.password);
            return body;
        },
        handleResponse: async (resp, report, c, retryCount) => {
            if (resp.data.code === 0) {
                const sucR = await report.updateOne({
                    $set: { push_status: 1, push_time: new Date() },
                    $inc: { try_times: 1 }
                });
                log.info(c.account + ' report push suc:' + sucR.modifiedCount, resp.config.data.phoneNumber, resp.config.data.errorCode, resp.config.data.bizId);
            } else {
                await retryPush(report, c, retryCount, resp.config.data);
            }
        }
    },
    hxtx: {
        generateData: (results) => {
            return results.map(item => {
                return {
                    appkey: item.cus_id,
                    phone: item.mobile,
                    status: item.status === 'DELIVRD' ? 0 : 2,
                    desc: item.status,
                    uid: item.msg_id,
                    report_time: item.report_date
                };
            });
        },
        handleResponse: async (resp, lock, c, retryCount) => {
            if (resp.data.code === '00000') {
                const sucR = await models.customerReport.updateMany({ lock }, {
                    $set: { push_status: 1, push_time: new Date() },
                    $inc: { try_times: 1 }
                });
                log.info(c.account + ' report push suc:' + sucR.modifiedCount, resp.config.data);
            } else {
                const reports = await models.customerReport.find({ lock });
                for (const report of reports) {
                    await retryPush(report, c, retryCount, resp.config.data);
                }
            }
        }
    },
    iqiyi: {
        generateData: (results) => {
            const reportArray = results.map(r => {
                return `${r.ticket},${r.mobile},${r.status},${r.report_date},${r.send_date},${r.pieces},${r.ext},${r.msg_id},${r.ref},${r.iso_code}`;
            });
            return { report: reportArray.join(';') };
        },
        handleResponse: async (resp, lock, c, retryCount) => {
            if (resp.data === 'success') {
                const sucR = await models.customerReport.updateMany({ lock }, {
                    $set: { push_status: 1, push_time: new Date() },
                    $inc: { try_times: 1 }
                });
                log.info(c.account + ' report push suc:' + sucR.modifiedCount, resp.config.data.report);
            } else {
                const reports = await models.customerReport.find({ lock });
                for (const report of reports) {
                    await retryPush(report, c, retryCount, resp.config.data);
                }
            }
        }
    }
};

// 重试间隔时间（毫秒）
const retryIntervals = [1 * 60 * 1000, 15 * 60 * 1000, 1 * 60 * 60 * 1000, 24 * 60 * 60 * 1000];

// 重试推送函数
async function retryPush(report, c, retryCount, data) {
    if (retryCount < retryIntervals.length) {
        await utils.sleep(retryIntervals[retryCount]);
        const strategy = pushStrategies[c.push_type] || pushStrategies.iqiyi;
        try {
            const headers = c.push_type === 'iqiyi' ? { "Content-Type": "application/x-www-form-urlencoded" } : {};
            const resp = await axios.post(c.report_url, data, { headers });
            await strategy.handleResponse(resp, report, c, retryCount + 1);
        } catch (error) {
            console.error('Retry error:', error);
            await report.updateOne({
                $set: { lock: '', push_status: 2, push_time: new Date() },
                $inc: { try_times: 1 }
            });
            log.info(c.account + ' report push fail after retry', data);
        }
    } else {
        await report.updateOne({
            $set: { lock: '', push_status: 2, push_time: new Date() },
            $inc: { try_times: 1 }
        });
        log.info(c.account + ' report push fail after all retries', data);
    }
}

// QPS 控制
let requestCount = 0;
const qpsLimit = 500;
const interval = 1000; // 1 秒

setInterval(() => {
    requestCount = 0;
}, interval);

async function pushReport() {
    while (true) {
        const cusList = await models.customer.find({ report_url: { $ne: '' } });
        for (let i = 0; i < 120; i++) {
            for (const c of cusList) {
                const q = { cus_id: c.account, try_times: { $lt: 3 }, push_status: { $ne: 1 }, lock: '' };
                const lock = i + c.account + new Date().getTime();
                const update = {
                    lock,
                    type: 1
                };
                const rows = await models.customerReport.updateMany(q, { $set: update }).limit(50);

                if (rows.modifiedCount > 0) {
                    const results = await models.customerReport.find({ lock });
                    const strategy = pushStrategies[c.push_type] || pushStrategies.iqiyi;

                    // 按 10 条数据一组进行分割
                    for (let j = 0; j < results.length; j += 10) {
                        const batch = results.slice(j, j + 10);
                        if (requestCount < qpsLimit) {
                            requestCount++;
                            if (c.push_type === 'cainiao') {
                                for (const report of batch) {
                                    const data = strategy.generateData(report, c);
                                    try {
                                        const resp = await axios.post(c.report_url, data);
                                        await strategy.handleResponse(resp, report, c, 0);
                                    } catch (error) {
                                        console.error('Error during push:', error);
                                        await retryPush(report, c, 0, data);
                                    }
                                }
                            } else {
                                const data = strategy.generateData(batch);
                                try {
                                    const headers = c.push_type === 'iqiyi' ? { "Content-Type": "application/x-www-form-urlencoded" } : {};
                                    const resp = await axios.post(c.report_url, data, { headers });
                                    await strategy.handleResponse(resp, lock, c, 0);
                                } catch (error) {
                                    console.error('Error during push:', error);
                                    for (const report of batch) {
                                        await retryPush(report, c, 0, data);
                                    }
                                }
                            }
                        } else {
                            await utils.sleep(1000); // 等待 1 秒
                            j -= 10; // 重新处理当前批次
                        }
                    }
                }
            }
            await utils.sleep(1000);
        }
    }
}    